package com.turbo.cloud.gateway.utils.jwt;

import cn.hutool.json.JSONUtil;
import io.jsonwebtoken.*;

import java.util.Base64;
import java.util.Date;
import java.util.Objects;
import java.util.UUID;

/**
 * 生成token以及校验token相关方法
 *
 * @author: zhangluning
 */
public class JwtUtils {

    private static final String JWT_PAYLOAD_USER_KEY = "user";
    private static final Long FORMAT_TIME_MS = 1000L;

    /**
     * 生成token
     *
     * @param userInfo   载荷中的数据
     * @param secret     秘钥
     * @param expiration 失效时间
     * @return JWT
     */
    public static String generateTokenExpire(Object userInfo, String secret, long expiration) {
        return Jwts.builder().claim(JWT_PAYLOAD_USER_KEY, JSONUtil.toJsonStr(userInfo)).setId(createJTI()).setExpiration(new Date(System.currentTimeMillis() + expiration * FORMAT_TIME_MS)).signWith(SignatureAlgorithm.HS512, secret).compact();
    }

    /**
     * 验证token
     *
     * @param token  用户请求中的token
     * @param secret 秘钥
     * @return 是否有效
     */
    public static boolean validateTokenExpire(String token, String secret) {
        Claims body = parserToken(token, secret);
        if (Objects.isNull(body)) {
            return false;
        }
        Date expiredDate = body.getExpiration();
        // 过期时间应该在当前时间之后视为有效
        boolean isExpire = expiredDate.after(new Date());
        return isExpire;
    }

    /**
     * 验证token
     *
     * @param userInfo   载荷中的数据
     * @param secret     秘钥
     * @param expiration 失效秒（s）
     * @param advance    预先秒（s）
     * @return 是否有效
     */
    public static String refreshTokenExpire(String token, Object userInfo, String secret, long expiration, long advance) {
        if (canRefreshTokenExpire(token, secret, advance)) {
            return generateTokenExpire(userInfo, secret, expiration);
        }
        return token;
    }

    /**
     * 是否能刷新token
     *
     * @param secret  秘钥
     * @param advance 预先秒（s）
     * @return 是否有效
     */
    public static boolean canRefreshTokenExpire(String token, String secret, long advance) {
        Claims body = parserToken(token, secret);
        if (Objects.isNull(body)) {
            return false;
        }
        Long expiredDateMs = body.getExpiration().getTime();
        Long nowDateMs = (new Date()).getTime();
        return expiredDateMs < (nowDateMs + advance * FORMAT_TIME_MS);
    }

    /**
     * 解析token
     *
     * @param token  用户请求中的token
     * @param secret 秘钥
     * @return Jws<Claims>
     */
    private static Claims parserToken(String token, String secret) {
        Claims claims;
        try {
            claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
        } catch (ExpiredJwtException e) {
            claims = e.getClaims();
        }
        return claims;
    }

    /**
     * 生成JWT ID
     */
    private static String createJTI() {
        return new String(Base64.getEncoder().encode(UUID.randomUUID().toString().getBytes()));
    }

    /**
     * 获取token中的用户信息
     *
     * @param token     用户请求中的令牌
     * @param secret    秘钥
     * @param userClass 用户实体类
     * @return 用户信息
     */
    public static <T> JwtPayload<T> getUserInfoFromToken(String token, String secret, Class<T> userClass) {
        Claims body = parserToken(token, secret);
        JwtPayload<T> claims = new JwtPayload<>();
        claims.setId(body.getId());
        String userInfoJson = body.get(JWT_PAYLOAD_USER_KEY).toString();
        claims.setUserInfo(JSONUtil.toBean(userInfoJson, userClass));
        claims.setExpiration(body.getExpiration());
        return claims;
    }

    /**
     * 获取token中的载荷信息
     *
     * @param token  用户请求中的令牌
     * @param secret 秘钥
     * @return 用户信息
     */
    public static <T> JwtPayload<T> getUserInfoFromToken(String token, String secret) {
        Claims body = parserToken(token, secret);
        JwtPayload<T> claims = new JwtPayload<>();
        claims.setId(body.getId());
        claims.setExpiration(body.getExpiration());
        return claims;
    }
}
